/*
 * Copyright (C) 2013 Zhenya Leonov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jpal.collect;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Set;

import org.junit.Test;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.PeekingIterator;
import com.google.common.collect.testing.IteratorFeature;
import com.google.common.collect.testing.IteratorTester;
import com.google.common.collect.testing.ListIteratorTester;
import com.google.common.testing.NullPointerTester;

public class MoreIteratorsTest {

	private static final List<Integer> IMMUTABLE_LIST = ImmutableList.of(1, 2,
			3, 4, 5, 6, 7, 8, 9);

	@Test
	public void testAsIterable() {
		final Iterator<Integer> iterator = Lists.newArrayList(1, 2, 3, 4, 5)
				.iterator();
		final Iterable<Integer> iterable = MoreIterators.asIterable(iterator);

		assertEquals(iterator, iterable.iterator());

		try {
			iterable.iterator();
			fail("Expected IllegalStateException");
		} catch (IllegalStateException e) {
		}
	}

	@Test
	public void testConcatEmpty() {
		new ListIteratorTester<Integer>(5, ImmutableList.of(0),
				IteratorFeature.UNMODIFIABLE,
				Collections.<Integer> emptyList(), 0) {

			@Override
			protected ListIterator<Integer> newTargetIterator() {
				return MoreIterators.concat();
			}
		}.test();
	}

	@Test
	public void testConcatEmptyListIterator() {
		new ListIteratorTester<Integer>(5, ImmutableList.of(0),
				IteratorFeature.UNMODIFIABLE,
				Collections.<Integer> emptyList(), 0) {

			@Override
			protected ListIterator<Integer> newTargetIterator() {
				return MoreIterators.concat(MoreIterators
						.<Integer> emptyListIterator());
			}
		}.test();
	}

	@Test
	public void testConcatMultipleEmptyListIterators() {
		new ListIteratorTester<Integer>(5, ImmutableList.of(0),
				IteratorFeature.UNMODIFIABLE,
				Collections.<Integer> emptyList(), 0) {

			@Override
			protected ListIterator<Integer> newTargetIterator() {
				return MoreIterators.concat(
						MoreIterators.<Integer> emptyListIterator(),
						MoreIterators.<Integer> emptyListIterator(),
						MoreIterators.<Integer> emptyListIterator());
			}
		}.test();
	}

	@Test
	public void testConcatMultipleListIterators() {
		new ListIteratorTester<Integer>(5, ImmutableList.of(0),
				IteratorFeature.UNMODIFIABLE, IMMUTABLE_LIST, 0) {

			@Override
			protected ListIterator<Integer> newTargetIterator() {
				return MoreIterators.concat(Arrays.asList(1).listIterator(),
						MoreIterators.<Integer> emptyListIterator(), Arrays
								.asList(2, 3, 4).listIterator(),
						Arrays.asList(5, 6).listIterator(),
						Arrays.asList(7, 8, 9).listIterator(), MoreIterators
								.<Integer> emptyListIterator());
			}
		}.test();
	}

	@Test
	public void testEmptyListIterator() {
		new ListIteratorTester<Integer>(5, Arrays.asList(0), ImmutableList.of(
				IteratorFeature.SUPPORTS_REMOVE, IteratorFeature.SUPPORTS_SET),
				Collections.<Integer> emptyList(), 0) {

			@Override
			protected ListIterator<Integer> newTargetIterator() {
				return MoreIterators.emptyListIterator();
			}
		}.test();
	}

	@Test
	public void testEmptyPeekingListIterator() {
		final PeekingIterator<Integer> peekItor = MoreIterators
				.emptyPeekingIterator();

		new IteratorTester<Integer>(5, ImmutableList.of(
				IteratorFeature.SUPPORTS_REMOVE, IteratorFeature.SUPPORTS_SET),
				Collections.<Integer> emptyList(),
				IteratorTester.KnownOrder.KNOWN_ORDER) {

			@Override
			protected Iterator<Integer> newTargetIterator() {
				return peekItor;
			}
		}.test();

		try {
			peekItor.peek();
			fail("Expected NoSuchElementException");
		} catch (NoSuchElementException e) {
		}
	}

	//
	// @Test
	// public void testFilterClass() {
	// final List<? extends Number> list = Lists.newArrayList(1, 1.5, 2, 2.5, 3,
	// 3.5);
	//
	// new ListIteratorTester(5, Arrays.asList(0), IteratorFeature.UNMODIFIABLE,
	// Arrays.asList(1,2,3), 0) {
	//
	// @Override
	// protected ListIterator<Integer> newTargetIterator() {
	// return MoreIterators.filter(list.listIterator(), Integer.class);
	// }
	// }.test();
	// }

	@Test
	public void testListIterator() {
		final List<Integer> list = IMMUTABLE_LIST;

		new ListIteratorTester<Integer>(5, ImmutableList.of(0),
				IteratorFeature.UNMODIFIABLE, list, 0) {

			@Override
			protected ListIterator<Integer> newTargetIterator() {
				return MoreIterators.listIterator(MoreSets.newLinkedHashSet(1,
						2, 3, 4, 5, 6, 7, 8, 9).iterator());
			}
		}.test();

		new ListIteratorTester<Integer>(5, ImmutableList.of(0),
				IteratorFeature.UNMODIFIABLE, list, 0) {

			@Override
			protected ListIterator<Integer> newTargetIterator() {
				return MoreIterators.listIterator(Lists.newArrayList(1, 2, 3,
						4, 5, 6, 7, 8, 9).iterator());
			}
		}.test();
	}

	@Test
	public void testListIteratorEmpty() {
		new ListIteratorTester<Integer>(5, ImmutableList.of(0),
				IteratorFeature.UNMODIFIABLE,
				Collections.<Integer> emptyList(), 0) {

			@Override
			protected ListIterator<Integer> newTargetIterator() {
				return MoreIterators.listIterator(Iterators
						.<Integer> emptyIterator());
			}
		}.test();
	}

	@Test
	public void testNullPointerExceptions() {
		NullPointerTester tester = new NullPointerTester();
		tester.testAllPublicStaticMethods(Lists.class);
	}

	@Test
	public void testReverseOrderCollection() {
		final List<Integer> reverse = Lists.newArrayList(9, 8, 7, 6, 5, 4, 3,
				2, 1);

		new IteratorTester<Integer>(5, IteratorFeature.UNMODIFIABLE,
				IMMUTABLE_LIST, IteratorTester.KnownOrder.KNOWN_ORDER) {

			@Override
			protected Iterator<Integer> newTargetIterator() {
				return MoreIterators.reverseOrder(reverse);
			}
		}.test();
	}

	@Test
	public void testReverseOrderCollectionEmpty() {

		new IteratorTester<Integer>(5, IteratorFeature.UNMODIFIABLE,
				Collections.<Integer> emptySet(),
				IteratorTester.KnownOrder.KNOWN_ORDER) {

			@Override
			protected Iterator<Integer> newTargetIterator() {
				return MoreIterators.reverseOrder(Collections
						.<Integer> emptySet());
			}
		}.test();
	}

	@Test
	public void testReverseOrderDeque() {
		final Deque<Integer> deque = MoreLists.newLinkedList(1, 2, 3, 4, 5);

		new IteratorTester<Integer>(5, IteratorFeature.UNMODIFIABLE,
				ImmutableSet.of(5, 4, 3, 2, 1),
				IteratorTester.KnownOrder.KNOWN_ORDER) {

			@Override
			protected Iterator<Integer> newTargetIterator() {
				return Iterators.unmodifiableIterator(MoreIterators
						.reverseOrder(deque));
			}
		}.test();

		assertEquals(MoreIterators.reverseOrder(deque).getClass()
				.getSimpleName(), "DescendingIterator");
	}

	@Test
	public void testReverseOrderIterator() {
		final List<Integer> reverse = Lists.newArrayList(9, 8, 7, 6, 5, 4, 3,
				2, 1);

		new IteratorTester<Integer>(5, IteratorFeature.UNMODIFIABLE,
				IMMUTABLE_LIST, IteratorTester.KnownOrder.KNOWN_ORDER) {

			@Override
			protected Iterator<Integer> newTargetIterator() {
				return MoreIterators.reverseOrder(reverse.iterator());
			}
		}.test();
	}

	@Test
	public void testReverseOrderIteratorEmpty() {

		new IteratorTester<Integer>(5, IteratorFeature.UNMODIFIABLE,
				Collections.<Integer> emptySet(),
				IteratorTester.KnownOrder.KNOWN_ORDER) {

			@Override
			protected Iterator<Integer> newTargetIterator() {
				return MoreIterators.reverseOrder(Iterators
						.<Integer> emptyIterator());
			}
		}.test();
	}

	@Test
	public void testReverseOrderNavigableSet() {
		final Set<Integer> set = MoreSets.newTreeSet(1, 2, 3, 4, 5);

		new IteratorTester<Integer>(5, IteratorFeature.UNMODIFIABLE,
				ImmutableSet.of(5, 4, 3, 2, 1),
				IteratorTester.KnownOrder.KNOWN_ORDER) {

			@Override
			protected Iterator<Integer> newTargetIterator() {
				return Iterators.unmodifiableIterator(MoreIterators
						.reverseOrder(set));
			}
		}.test();

		assertEquals(
				MoreIterators.reverseOrder(set).getClass().getSimpleName(),
				"DescendingSubMapKeyIterator");
	}

	@Test
	public void testUnmodifiableListIterator() {
		new ListIteratorTester<Integer>(5, ImmutableList.of(0),
				IteratorFeature.UNMODIFIABLE, IMMUTABLE_LIST, 0) {

			@Override
			protected ListIterator<Integer> newTargetIterator() {
				return MoreIterators.unmodifiable(Lists.newArrayList(1, 2, 3,
						4, 5, 6, 7, 8, 9).listIterator());
			}
		}.test();
	}

	@Test
	public void testUnmodifiableListIteratorEmpty() {
		new ListIteratorTester<Integer>(5, ImmutableList.of(0),
				IteratorFeature.UNMODIFIABLE,
				Collections.<Integer> emptyList(), 0) {

			@Override
			protected ListIterator<Integer> newTargetIterator() {
				return MoreIterators.unmodifiable(Lists
						.<Integer> newArrayList().listIterator());
			}
		}.test();
	}

	@Test
	public void testUnmodifiablePeekingIterator() {
		final List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);

		new IteratorTester<Integer>(5, IteratorFeature.UNMODIFIABLE, list,
				IteratorTester.KnownOrder.KNOWN_ORDER) {

			@Override
			protected Iterator<Integer> newTargetIterator() {
				return MoreIterators.unmodifiable(Iterators
						.peekingIterator(list.iterator()));
			}
		}.test();

		final PeekingIterator<Integer> peekItor = MoreIterators
				.unmodifiable(Iterators.peekingIterator(list.iterator()));

		assertTrue(peekItor instanceof PeekingIterator);
		assertTrue(peekItor.peek().equals(1));
		peekItor.next();
		peekItor.next();
		peekItor.next();
		peekItor.next();

		assertTrue(peekItor.peek().equals(5));

		peekItor.next();

		try {
			MoreIterators.unmodifiable(peekItor).peek();
			fail("Expected NoSuchElementException");
		} catch (NoSuchElementException e) {
		}
	}

	@Test
	public void testUnmodifiablePeekingIteratorEmpty() {
		final List<Integer> list = Lists.newArrayList();

		new IteratorTester<Integer>(5, IteratorFeature.UNMODIFIABLE, list,
				IteratorTester.KnownOrder.KNOWN_ORDER) {

			@Override
			protected Iterator<Integer> newTargetIterator() {
				return MoreIterators.unmodifiable(Iterators
						.peekingIterator(list.iterator()));
			}
		}.test();

		final PeekingIterator<Integer> peekItor = MoreIterators
				.unmodifiable(Iterators.peekingIterator(list.iterator()));

		assertTrue(peekItor instanceof PeekingIterator);

		try {
			MoreIterators.unmodifiable(peekItor).peek();
			fail("Expected NoSuchElementException");
		} catch (NoSuchElementException e) {
		}
	}

}